package be.kuleuven.thesis.databases;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;

import be.kuleuven.thesis.framework.Atmosphere;
import be.kuleuven.thesis.framework.Filter;
import be.kuleuven.thesis.framework.Song;
import be.kuleuven.thesis.status.PlaylistGenerator;

public class AristoMusicManager {
	private static final String USER_NAME = "KUL5794";
	private static final String USER_PASSW = "874cd93cbd24516509cc6ecdf2dc11b3";
	private static final String APP_ID = "7";
	private static final String APP_PASSW = "m4rc3llu5w4ll4c3";
	private static final String CLIENT_VERSION = "2.006.02";
	private static final String NAMESPACE = "http://www.tunify.com";
	private static final String URL = "http://85.119.222.49/blackbox2-1.6.0/services/tunify2?wsdl";
	private static AristoMusicManager instance = new AristoMusicManager();;
	private SoapObject credentials;

	private AristoMusicManager() {
		credentials = new SoapObject(null, "credentials");
		credentials.addProperty("userID", "");
		credentials.addProperty("userPassword", USER_PASSW);
		credentials.addProperty("appID", APP_ID);
		credentials.addProperty("appPassword", APP_PASSW);
		credentials.addProperty("sessionCode", "");
	}

	public static AristoMusicManager getInstance() {
		return instance;
	}
	
	public void logIn() {
		SoapObject request = new SoapObject(NAMESPACE, "loginUser");
		request.addProperty("userName", USER_NAME); //variable name, value. I got the variable name, from the wsdl file!
		request.addProperty("userPassword", USER_PASSW);
		request.addProperty("appID", APP_ID);
		request.addProperty("appPassword", APP_PASSW);
		request.addProperty("clientVersion", CLIENT_VERSION);
		SoapObject result = sendWSRequest(request, "loginUser");
		SoapObject configuration = (SoapObject) result.getProperty("configuration");
		
		credentials = new SoapObject(null, "credentials");
		credentials.addProperty("userID", configuration.getPropertyAsString("userID"));
		credentials.addProperty("userPassword", USER_PASSW);
		credentials.addProperty("appID", APP_ID);
		credentials.addProperty("appPassword", APP_PASSW);
		credentials.addProperty("sessionCode", result.getPropertyAsString("sessionCode"));
	}
	
	public boolean isLoggedIn() {
		return credentials.hasProperty("sessionCode");
	}
	
	public List<Song> search(String query) {
		String[] words = query.split("\\s");
		String sendQuery = words[0] + "*";
		for(int i=1; i<words.length; i++)
			sendQuery = sendQuery + " " + words[i] + "*";
		SoapObject request = new SoapObject(NAMESPACE, "searchAudioFilesByValue");
		request.addSoapObject(credentials);
		request.addProperty("searchValue",sendQuery);
		
		SoapObject result = sendWSRequest(request, "searchAudioFilesByValue");
		if(result == null)
			return null;
		if(isValidResult(result))
			return getAudioFiles(result);
		else return search(query);
	}
	
	public List<Song> getAudioFiles(SoapObject result) {
		int rr = result.getPropertyCount();
		List<Song> resultSet = new ArrayList<Song>();
		for(int i = 1; i < rr; i++) {
			SoapObject audioFile = (SoapObject) result.getProperty(i);
			resultSet.add(audioFileToSong(audioFile));
		}
		return resultSet;
	}
	
	public Song audioFileToSong(SoapObject audioFile) {
		Song song = new Song();
		song.setId(audioFile.getPropertyAsString("id"));
		song.setDuration(new Integer(audioFile.getPropertyAsString("duration")));
		song.setArtist(audioFile.getPropertyAsString("group"));
		song.setLocation(audioFile.getPropertyAsString("location"));
		String title = audioFile.getPropertyAsString("title");
		String regex = "\\(Akoestisch\\)";
		Pattern pattern = Pattern.compile(regex);
		title = pattern.split(title)[0];
		song.setTitle(title);
		song.setOrigin(audioFile.getPropertyAsString("origin"));
		song.setBpm(new Float(audioFile.getPropertyAsString("bpm")));
		//song.setLocalAvailable(new Boolean(audioFile.getPropertyAsString("localAvailable")));
		song.setVersion(audioFile.getPropertyAsString("version"));
		song.setYear(new Integer(audioFile.getPropertyAsString("year")));
		song.setMood(audioFile.getPropertyAsString("mood"));
		song.setDancingStyle(audioFile.getPropertyAsString("dancingStyle"));
		song.setStartAudioSignal(new Integer(audioFile.getPropertyAsString("startAudioSignal")));
		song.setEndAudioSignal(new Integer(audioFile.getPropertyAsString("endAudioSignal")));
		song.setMusicStyle(audioFile.getPropertyAsString("musicStyle"));
		return song;
	}
	
	public String getUrl(Song song) {
		SoapObject request = new SoapObject(NAMESPACE, "getHttpUrlForAudioFile");
		request.addSoapObject(credentials);
		SoapObject audioFile = new SoapObject(null, "audioFile");
		audioFile.addAttribute("i:type", "n0:Song");
		audioFile.addProperty("id",song.getId());
		audioFile.addProperty("duration", song.getDuration());
		audioFile.addProperty("group",song.getArtist());
		audioFile.addProperty("location", song.getLocation());
		audioFile.addProperty("title", song.getTitle());
		audioFile.addProperty("origin", song.getOrigin());
		audioFile.addProperty("bpm", song.getBpm()+"");
		audioFile.addProperty("localAvailable", song.isLocalAvailable());
		audioFile.addProperty("version", song.getVersion());
		audioFile.addProperty("year", song.getYear());
		audioFile.addProperty("mood", song.getMood());
		audioFile.addProperty("dancingStyle", song.getDancingStyle());
		audioFile.addProperty("startAudioSignal", song.getStartAudioSignal());
		audioFile.addProperty("endAudioSignal", song.getEndAudioSignal());
		audioFile.addProperty("musicStyle", song.getMusicStyle());
		request.addSoapObject(audioFile);
		SoapObject result = sendWSRequest(request, "getHttpUrlForAudioFile");
		if(isValidResult(result))
			return result.getPropertyAsString("url");
		else return getUrl(song);
	}
	
	private boolean isValidResult(SoapObject result) {
		SoapObject status = (SoapObject) result.getProperty("status");
		String code = status.getPropertyAsString("statusCode");
		if(code.equals("OK") || code.equals("searchValueTooShort"))
			return true;
		if(code.equals("duplicateUsage") || code.equals("invalidUserID")) {
			logIn();
			return false;
		}
		System.out.println("Oei waarom is da result hier invalid");
		return false;
	}
	
	public List<Song> getGeneratedSongs(Set<Song> seeds, Filter filter) {
		String method = "getAudioFilesForMusicCollection";
		SoapObject request = new SoapObject(NAMESPACE, method);
		request.addSoapObject(credentials);
		request.addProperty("mcID", "5115");
		request.addSoapObject(
				getChangeableIntervalParameter("BPM", Filter.MIN_TEMPO, Filter.MAX_TEMPO, filter.getMinTempo(), filter.getMaxTempo()));
		request.addSoapObject(
				getChangeableIntervalParameter("Bekendheid", Filter.MIN_POP+1, Filter.MAX_POP+1, filter.getMinPop()+1, filter.getMaxPop()+1));
		request.addSoapObject(
				getChangeableIntervalParameter("Hardheid", Filter.MIN_HARD, Filter.MAX_HARD, filter.getMinHard(), filter.getMaxHard()));
		int year = filter.getMinYear();
		if(year == 1949)
			year = 1920;
		request.addSoapObject(
				getChangeableIntervalParameter("Jaar", 1920, Filter.MAX_YEAR, filter.getMinYear(), filter.getMaxYear()));
		SoapObject sfeer = new SoapObject(null, "changeableParameter");
		sfeer.addAttribute("PresentationType", "selection");
		sfeer.addAttribute("name", "Sfeer");
		sfeer.addAttribute("i:type", "n0:ChangeableSelectionParameter");
		for(int i=0; i < Atmosphere.values().length; i++) {
			SoapObject value = new SoapObject(null, "value");
			//Atmosphere atmo = Atmosphere.getAtmosphere(i);
			Atmosphere atmo = Atmosphere.values()[i];
			value.addAttribute("isSelected", filter.getAtmospheres().contains(atmo));
			value.addAttribute("value", atmo.toString());
			sfeer.addSoapObject(value);
		}
		request.addSoapObject(sfeer);
		int duration = PlaylistGenerator.getInstance().getLikedSongsDuration() + PlaylistGenerator.getInstance().getDuration();
		request.addProperty("duration", duration);
		request.addProperty("saveChangeableParameters", true);
		for(Song song: seeds) {
			request.addProperty("seedAudioFileIDs", song.getId());
		}
		SoapObject result = sendWSRequest(request, method);
		if(result == null)
			return null;
		if(isValidResult(result))
			return getAudioFiles(result);
		else return getGeneratedSongs(seeds, filter);
	}
	
	public SoapObject getChangeableIntervalParameter(String name, int minimum, int maximum, int lowerSelection, int upperSelection) {
		SoapObject changeableParameter = new SoapObject(null, "changeableParameter");
		changeableParameter.addAttribute("presentationType","interval");
		changeableParameter.addAttribute("name", name);
		changeableParameter.addAttribute("i:type", "n0:ChangeableIntervalParameter");
		changeableParameter.addProperty("minimum", minimum);
		changeableParameter.addProperty("maximum", maximum);
		changeableParameter.addProperty("step", "1");
		changeableParameter.addProperty("lowerSelection", lowerSelection);
		changeableParameter.addProperty("upperSelection", upperSelection);
		return changeableParameter;
	}

	private SoapObject sendWSRequest(SoapObject request, String methodName){
		try {
			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); //put all required data into a soap envelope
			envelope.setOutputSoapObject(request);  //prepare request
			
			//AndroidHttpTransport httpTransport = new AndroidHttpTransport(URL);  
			HttpTransportSE httpTransport = new HttpTransportSE(URL);
			httpTransport.debug = true;  //this is optional, use it if you don't want to use a packet sniffer to check what the sent 
			//message was (httpTransport.requestDump)
			String soapAction = NAMESPACE + "/" + methodName;
			
			httpTransport.call(soapAction, envelope); //send request
			System.out.println(envelope.bodyOut.toString());
			System.out.println(envelope.bodyIn.toString());
			SoapObject result=(SoapObject)envelope.bodyIn; //get response
			return result;
		} catch (SoapFault e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (XmlPullParserException e) {
			e.printStackTrace();
			return null;
		}
	}
	
}
